/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1997 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the Computer Systems
 *	Engineering Group at Lawrence Berkeley Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * @(#) $Header: /cvsroot/nsnam/ns-2/common/packet.h,v 1.100 2006/02/22 13:32:23 mahrenho Exp $ (LBL)
 */

#ifndef ns_packet_h
#define ns_packet_h

#include <string.h>
#include <assert.h>

#include "config.h"
#include "scheduler.h"
#include "object.h"
#include "lib/bsd-list.h"
#include "packet-stamp.h"
#include "ns-process.h"

// Used by wireless routing code to attach routing agent
#define RT_PORT		255	/* port that all route msgs are sent to */

#define HDR_CMN(p)      (hdr_cmn::access(p))
#define HDR_ARP(p)      (hdr_arp::access(p))
#define HDR_MAC(p)      (hdr_mac::access(p))
#define HDR_MAC802_11(p) ((hdr_mac802_11 *)hdr_mac::access(p))
#define HDR_MAC_TDMA(p) ((hdr_mac_tdma *)hdr_mac::access(p))
#define HDR_STRATEGY(p) ((hdr_strategy *)hdr_mac::access(p))
#define HDR_SMAC(p)     ((hdr_smac *)hdr_mac::access(p))
#define HDR_LL(p)       (hdr_ll::access(p))
#define HDR_HDLC(p)     ((hdr_hdlc *)hdr_ll::access(p))
#define HDR_IP(p)       (hdr_ip::access(p))
#define HDR_RTP(p)      (hdr_rtp::access(p))
#define HDR_TCP(p)      (hdr_tcp::access(p))
#define HDR_SCTP(p)     (hdr_sctp::access(p))
#define HDR_SR(p)       (hdr_sr::access(p))
#define HDR_TFRC(p)     (hdr_tfrc::access(p))
#define HDR_TORA(p)     (hdr_tora::access(p))
#define HDR_IMEP(p)     (hdr_imep::access(p))
#define HDR_CDIFF(p)    (hdr_cdiff::access(p))  /* chalermak's diffusion*/
//#define HDR_DIFF(p)     (hdr_diff::access(p))  /* SCADD's diffusion ported into ns */
#define HDR_LMS(p)		(hdr_lms::access(p))

/* --------------------------------------------------------------------*/

enum packet_t {
		PT_TCP,
		PT_UDP,
		PT_CBR,
		PT_AUDIO,
		PT_VIDEO,
		PT_ACK,
		PT_START,
		PT_STOP,
		PT_PRUNE,
		PT_GRAFT,
		PT_GRAFTACK,
		PT_JOIN,
		PT_ASSERT,
		PT_MESSAGE,
		PT_RTCP,
		PT_RTP,
		PT_RTPROTO_DV,
		PT_CtrMcast_Encap,
		PT_CtrMcast_Decap,
		PT_SRM,
		/* simple signalling messages */
		PT_REQUEST,	
		PT_ACCEPT,	
		PT_CONFIRM,	
		PT_TEARDOWN,	
		PT_LIVE,	// packet from live network
		PT_REJECT,

		PT_TELNET,	// not needed: telnet use TCP
		PT_FTP,
		PT_PARETO,
		PT_EXP,
		PT_INVAL,
		PT_HTTP,

		/* new encapsulator */
		PT_ENCAPSULATED,
		PT_MFTP,

		/* CMU/Monarch's extnsions */
		PT_ARP,      //34
		PT_MAC,
		PT_TORA,
		PT_DSR,
		PT_AODV,  //38
		PT_IMEP,

		// RAP packets
		PT_RAP_DATA,
		PT_RAP_ACK,

		PT_TFRC,
		PT_TFRC_ACK,
		PT_PING,

		// Diffusion packets - Chalermek
		PT_DIFF,

		// LinkState routing update packets
		PT_RTPROTO_LS,

		// MPLS LDP header
		PT_LDP,

		// GAF packet
		PT_GAF,  

		// ReadAudio traffic
		PT_REALAUDIO,

		// Pushback Messages
		PT_PUSHBACK,

#ifdef HAVE_STL
		// Pragmatic General Multicast
		PT_PGM,
#endif //STL

		// LMS packets
		PT_LMS,
		PT_LMS_SETUP,

		PT_SCTP,
		PT_SCTP_APP1,

		// SMAC packet
		PT_SMAC,

		// XCP packet
		PT_XCP,

		// HDLC packet
		PT_HDLC,

		// Bell Labs Traffic Trace Type (PackMime OL)
		PT_BLTRACE, 

		PT_WCETT, //carmen
		PT_GPSR,  //gpsr
		PT_MFLOOD,//mflood

		// insert new packet types here
		PT_NTYPE // This MUST be the LAST one
};

class p_info {
		public:
				p_info() {
						name_[PT_TCP]= "tcp";
						name_[PT_UDP]= "udp";
						name_[PT_CBR]= "cbr";
						name_[PT_AUDIO]= "audio";
						name_[PT_VIDEO]= "video";
						name_[PT_ACK]= "ack";
						name_[PT_START]= "start";
						name_[PT_STOP]= "stop";
						name_[PT_PRUNE]= "prune";
						name_[PT_GRAFT]= "graft";
						name_[PT_GRAFTACK]= "graftAck";
						name_[PT_JOIN]= "join";
						name_[PT_ASSERT]= "assert";
						name_[PT_MESSAGE]= "message";
						name_[PT_RTCP]= "rtcp";
						name_[PT_RTP]= "rtp";
						name_[PT_RTPROTO_DV]= "rtProtoDV";
						name_[PT_CtrMcast_Encap]= "CtrMcast_Encap";
						name_[PT_CtrMcast_Decap]= "CtrMcast_Decap";
						name_[PT_SRM]= "SRM";

						name_[PT_REQUEST]= "sa_req";	
						name_[PT_ACCEPT]= "sa_accept";
						name_[PT_CONFIRM]= "sa_conf";
						name_[PT_TEARDOWN]= "sa_teardown";
						name_[PT_LIVE]= "live"; 
						name_[PT_REJECT]= "sa_reject";

						name_[PT_TELNET]= "telnet";
						name_[PT_FTP]= "ftp";
						name_[PT_PARETO]= "pareto";
						name_[PT_EXP]= "exp";
						name_[PT_INVAL]= "httpInval";
						name_[PT_HTTP]= "http";
						name_[PT_ENCAPSULATED]= "encap";
						name_[PT_MFTP]= "mftp";
						name_[PT_ARP]= "ARP";
						name_[PT_MAC]= "MAC";
						name_[PT_TORA]= "TORA";
						name_[PT_DSR]= "DSR";
						name_[PT_AODV]= "AODV";
						name_[PT_IMEP]= "IMEP";
						name_[PT_WCETT]= "WCETT";          

						name_[PT_RAP_DATA] = "rap_data";
						name_[PT_RAP_ACK] = "rap_ack";

						name_[PT_TFRC]= "tcpFriend";
						name_[PT_TFRC_ACK]= "tcpFriendCtl";
						name_[PT_PING]="ping";

						/* For diffusion : Chalermek */
						name_[PT_DIFF] = "diffusion";

						// Link state routing updates
						name_[PT_RTPROTO_LS] = "rtProtoLS";

						// MPLS LDP packets
						name_[PT_LDP] = "LDP";

						// for GAF
						name_[PT_GAF] = "gaf";      

						// RealAudio packets
						name_[PT_REALAUDIO] = "ra";

						//pushback 
						name_[PT_PUSHBACK] = "pushback";

#ifdef HAVE_STL
						// for PGM
						name_[PT_PGM] = "PGM";
#endif //STL

						// LMS entries
						name_[PT_LMS]="LMS";
						name_[PT_LMS_SETUP]="LMS_SETUP";

						name_[PT_SCTP]= "sctp";
						name_[PT_SCTP_APP1] = "sctp_app1";

						// smac
						name_[PT_SMAC]="smac";

						// HDLC
						name_[PT_HDLC]="HDLC";

						// XCP
						name_[PT_XCP]="xcp";

						// Bell Labs (PackMime OL)
						name_[PT_BLTRACE]="BellLabsTrace";
						//mflood
						name_[PT_MFLOOD]="MFlood";

						// GPSR 
						name_[PT_GPSR]="gpsr";

						name_[PT_NTYPE]= "undefined";
				}
				const char* name(packet_t p) const { 
						if ( p <= PT_NTYPE ) return name_[p];
						return 0;
				}
				static bool data_packet(packet_t type) {
						return ( (type) == PT_TCP || \
										(type) == PT_TELNET || \
										(type) == PT_CBR || \
										(type) == PT_AUDIO || \
										(type) == PT_VIDEO || \
										(type) == PT_ACK || \
										(type) == PT_SCTP || \
										(type) == PT_SCTP_APP1 || \
										(type) == PT_HDLC \
							   );
				}
		private:
				static char* name_[PT_NTYPE+1];
};
extern p_info packet_info; /* map PT_* to string name */
//extern char* p_info::name_[];


#define DATA_PACKET(type) ( (type) == PT_TCP || \
				(type) == PT_TELNET || \
				(type) == PT_CBR || \
				(type) == PT_AUDIO || \
				(type) == PT_VIDEO || \
				(type) == PT_ACK || \
				(type) == PT_SCTP || \
				(type) == PT_SCTP_APP1 \
				)


//#define OFFSET(type, field)	((long) &((type *)0)->field)
#define OFFSET(type, field) ( (char *)&( ((type *)256)->field )  - (char *)256)

class PacketData : public AppData {
		public:
				PacketData(int sz) : AppData(PACKET_DATA) {
						datalen_ = sz;
						if (datalen_ > 0)
								data_ = new unsigned char[datalen_];
						else
								data_ = NULL;
				}
				PacketData(PacketData& d) : AppData(d) {
						datalen_ = d.datalen_;
						if (datalen_ > 0) {
								data_ = new unsigned char[datalen_];
								memcpy(data_, d.data_, datalen_);
						} else
								data_ = NULL;
				}
				virtual ~PacketData() { 
						if (data_ != NULL) 
								delete []data_; 
				}
				unsigned char* data() { return data_; }

				virtual int size() const { return datalen_; }
				virtual AppData* copy() { return new PacketData(*this); }
		private:
				unsigned char* data_;
				int datalen_;
};

//Monarch ext
typedef void (*FailureCallback)(Packet *,void *);

class Packet : public Event {
		private:
				unsigned char* bits_;	// header bits
				//	unsigned char* data_;	// variable size buffer for 'data'
				//  	unsigned int datalen_;	// length of variable size buffer
				AppData* data_;		// variable size buffer for 'data'
				static void init(Packet*);     // initialize pkt hdr 
				bool fflag_;
		protected:
				static Packet* free_;	// packet free list
				int	ref_count_;	// free the pkt until count to 0
		public:
				Packet* next_;		// for queues and the free list
				static int hdrlen_;

				Packet() : bits_(0), data_(0), ref_count_(0), next_(0) { }
				inline unsigned char* const bits() { return (bits_); }
				inline Packet* copy() const;
				inline Packet* refcopy() { ++ref_count_; return this; }
				inline int& ref_count() { return (ref_count_); }
				static inline Packet* alloc();
				static inline Packet* alloc(int);
				inline void allocdata(int);
				// dirty hack for diffusion data
				inline void initdata() { data_  = 0;}
				static inline void free(Packet*);
				inline unsigned char* access(int off) const {
						if (off < 0)
								abort();
						return (&bits_[off]);
				}
				// This is used for backward compatibility, i.e., assuming user data
				// is PacketData and return its pointer.
				inline unsigned char* accessdata() const { 
						if (data_ == 0)
								return 0;
						assert(data_->type() == PACKET_DATA);
						return (((PacketData*)data_)->data()); 
				}
				// This is used to access application-specific data, not limited 
				// to PacketData.
				inline AppData* userdata() const {
						return data_;
				}
				inline void setdata(AppData* d) { 
						if (data_ != NULL)
								delete data_;
						data_ = d; 
				}
				inline int datalen() const { return data_ ? data_->size() : 0; }

				// Monarch extn

				static void dump_header(Packet *p, int offset, int length);

				// the pkt stamp carries all info about how/where the pkt
				// was sent needed for a receiver to determine if it correctly
				// receives the pkt
				PacketStamp	txinfo_;  

				/*
				 * According to cmu code:
				 * This flag is set by the MAC layer on an incoming packet
				 * and is cleared by the link layer.  It is an ugly hack, but
				 * there's really no other way because NS always calls
				 * the recv() function of an object.
				 * 
				 */
				u_int8_t        incoming;

				//monarch extns end;
};

/* 
 * static constant associations between interface special (negative) 
 * values and their c-string representations that are used from tcl
 */
class iface_literal {
		public:
				enum iface_constant { 
						UNKN_IFACE= -1, /* 
										 * iface value for locally originated packets 
										 */
						ANY_IFACE= -2   /* 
										 * hashnode with iif == ANY_IFACE_   
										 * matches any pkt iface (imported from TCL);
										 * this value should be different from 
										 * hdr_cmn::UNKN_IFACE (packet.h)
										 */ 
				};
				iface_literal(const iface_constant i, const char * const n) : 
						value_(i), name_(n) {}
				inline int value() const { return value_; }
				inline const char * const name() const { return name_; }
		private:
				const iface_constant value_;
				/* strings used in TCL to access those special values */
				const char * const name_; 
};

static const iface_literal UNKN_IFACE(iface_literal::UNKN_IFACE, "?");
static const iface_literal ANY_IFACE(iface_literal::ANY_IFACE, "*");

/*
 * Note that NS_AF_* doesn't necessarily correspond with
 * the constants used in your system (because many
 * systems don't have NONE or ILINK).
 */
enum ns_af_enum { NS_AF_NONE, NS_AF_ILINK, NS_AF_INET };

struct rtdecision{
		nsaddr_t nodeid;
		int channel;
};
struct hdr_cmn {
		enum dir_t { DOWN= -1, NONE= 0, UP= 1 };
		packet_t ptype_;	// packet type (see above)
		int	size_;		// simulated packet size
		int	uid_;		// unique id
		int	error_;		// error flag
		int     errbitcnt_;     // # of corrupted bits jahn
		int     fecsize_;
		double	ts_;		// timestamp: for q-delay measurement
		int	iface_;		// receiving interface (label)
		dir_t	direction_;	// direction: 0=none, 1=up, -1=down
		// source routing 
		char src_rt_valid;
		double ts_arr_; // Required by Marker of JOBS 

		//Monarch extn begins
		nsaddr_t prev_hop_;     // IP addr of forwarding hop
		nsaddr_t next_hop_;	// next hop for this packet
		int      addr_type_;    // type of next_hop_ addr
		nsaddr_t last_hop_;     // for tracing on multi-user channels

		// called if pkt can't obtain media or isn't ack'd. not called if
		// droped by a queue
		FailureCallback xmit_failure_; 
		void *xmit_failure_data_;

		/*
		 * MONARCH wants to know if the MAC layer is passing this back because
		 * it could not get the RTS through or because it did not receive
		 * an ACK.
		 */
		int     xmit_reason_;
#define XMIT_REASON_RTS 0x01
#define XMIT_REASON_ACK 0x02

		// filled in by GOD on first transmission, used for trace analysis
		int num_forwards_;	// how many times this pkt was forwarded
		int opt_num_forwards_;   // optimal #forwards
		// Monarch extn ends;

		// tx time for this packet in sec
		double txtime_;
		inline double& txtime() { return(txtime_); }

		static int offset_;	// offset for this header
		inline static int& offset() { return offset_; }
		inline static hdr_cmn* access(const Packet* p) {
				return (hdr_cmn*) p->access(offset_);
		}

		/* per-field member functions */
		inline packet_t& ptype() { return (ptype_); }
		inline int& size() { return (size_); }
		inline int& uid() { return (uid_); }
		inline int& error() { return error_; }
		inline int& errbitcnt() {return errbitcnt_; }
		inline int& fecsize() {return fecsize_; }
		inline double& timestamp() { return (ts_); }
		inline int& iface() { return (iface_); }
		inline dir_t& direction() { return (direction_); }
		// monarch_begin
		inline nsaddr_t& next_hop() { return (next_hop_); }
		inline int& addr_type() { return (addr_type_); }
		inline int& num_forwards() { return (num_forwards_); }
		inline int& opt_num_forwards() { return (opt_num_forwards_); }
		//monarch_end
		//carmen
		int channelindex_;//annotated by zhp.passed through negotiate
		int interface;
		int localif_;//which is different from the iface....//annotated by zhp.passed through channel level
		inline int& localif() { return (localif_); }
		//WCETT
		rtdecision rtdecision_[20];
		int        backwardrt; //direction indication in rtdecision
		//carmen end
};


class PacketHeaderClass : public TclClass {
		protected:
				PacketHeaderClass(const char* classname, int hdrsize);
				virtual int method(int argc, const char*const* argv);
				void field_offset(const char* fieldname, int offset);
				inline void bind_offset(int* off) { offset_ = off; }
				inline void offset(int* off) {offset_= off;}
				int hdrlen_;		// # of bytes for this header
				int* offset_;		// offset for this header
		public:
				virtual void bind();
				virtual void export_offsets();
				TclObject* create(int argc, const char*const* argv);
};


inline void Packet::init(Packet* p)
{
		bzero(p->bits_, hdrlen_);
}

inline Packet* Packet::alloc()
{
		Packet* p = free_;
		if (p != 0) {
				assert(p->fflag_ == FALSE);
				free_ = p->next_;
				assert(p->data_ == 0);
				p->uid_ = 0;
				p->time_ = 0;
		} else {
				p = new Packet;
				p->bits_ = new unsigned char[hdrlen_];
				if (p == 0 || p->bits_ == 0)
						abort();
		}
		init(p); // Initialize bits_[]
		(HDR_CMN(p))->next_hop_ = -2; // -1 reserved for IP_BROADCAST
		(HDR_CMN(p))->last_hop_ = -2; // -1 reserved for IP_BROADCAST
		p->fflag_ = TRUE;
		(HDR_CMN(p))->direction() = hdr_cmn::DOWN;
		/* setting all direction of pkts to be downward as default; 
		   until channel changes it to +1 (upward) */
		p->next_ = 0;

		//carmen
		//init channel index to 0 and interface to 0
		(HDR_CMN(p))->channelindex_ = 0;
		(HDR_CMN(p))->interface=0;
		//
		//WCETT
		for(int i=0;i<20;i++){
				(HDR_CMN(p))->rtdecision_[i].nodeid=-1;
				(HDR_CMN(p))->rtdecision_[i].channel=-2;
		}
		(HDR_CMN(p))->backwardrt=0;
		return (p);
		//carmen end

}

/* 
 * Allocate an n byte data buffer to an existing packet 
 * 
 * To set application-specific AppData, use Packet::setdata()
 */
inline void Packet::allocdata(int n)
{
		assert(data_ == 0);
		data_ = new PacketData(n);
		if (data_ == 0)
				abort();
}

/* allocate a packet with an n byte data buffer */
inline Packet* Packet::alloc(int n)
{
		Packet* p = alloc();
		if (n > 0) 
				p->allocdata(n);
		return (p);
}


inline void Packet::free(Packet* p)
{
		if (p->fflag_) {
				if (p->ref_count_ == 0) {
						/*
						 * A packet's uid may be < 0 (out of a event queue), or
						 * == 0 (newed but never gets into the event queue.
						 */
						assert(p->uid_ <= 0);
						// Delete user data because we won't need it any more.
						if (p->data_ != 0) {
								delete p->data_;
								p->data_ = 0;
						}
						init(p);
						p->next_ = free_;
						free_ = p;
						p->fflag_ = FALSE;
				} else {
						--p->ref_count_;
				}
		}
}

inline Packet* Packet::copy() const
{

		Packet* p = alloc();
		memcpy(p->bits(), bits_, hdrlen_);
		if (data_) 
				p->data_ = data_->copy();
		p->txinfo_.init(&txinfo_);

		return (p);
}

		inline void
Packet::dump_header(Packet *p, int offset, int length)
{
		assert(offset + length <= p->hdrlen_);
		struct hdr_cmn *ch = HDR_CMN(p);

		fprintf(stderr, "\nPacket ID: %d\n", ch->uid());

		for(int i = 0; i < length ; i+=16) {
				fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
								p->bits_[offset + i],     p->bits_[offset + i + 1],
								p->bits_[offset + i + 2], p->bits_[offset + i + 3],
								p->bits_[offset + i + 4], p->bits_[offset + i + 5],
								p->bits_[offset + i + 6], p->bits_[offset + i + 7],
								p->bits_[offset + i + 8], p->bits_[offset + i + 9],
								p->bits_[offset + i + 10], p->bits_[offset + i + 11],
								p->bits_[offset + i + 12], p->bits_[offset + i + 13],
								p->bits_[offset + i + 14], p->bits_[offset + i + 15]);
		}
}

#endif
